package com.depth.viewer.utils.json
{
	/**
	 * @example ActionScript to use JSONDecoder: 
	 * <listing version="3.0">
	 * var s:String=null;
	 * var jSONDecoder:JSONDecoder = new JSONDecoder(s);
	 * </listing>
	 *
	 **/
	
	public class JSONDecoder
	{
		
		private var value:Object;
		private var tokenizer:JSONTokenizer;
		private var token:JSONToken;
		
		public function JSONDecoder(s:String=null)
		{
			if(s!=null){
				this.tokenizer = new JSONTokenizer(s);
				this.nextToken1();
				this.value = this.parseValue();
				return;
			}
			
		}
		
		private var chr:int;

	    private var tok:int;
	
	    private var src:String;
	
	    private var lastPos:int;
	
	    private var nextPos:int;
	
	    private var cachedChr:Boolean;
	
	    private var cachedTok:Boolean;
	
	    
	
	    public function decode(str:String):* {
	
	        var val:*;
	
	        src=str;
	
	        nextPos=0;
	
	        cachedChr=false;
	
	        cachedTok=false;
	
	        val=nextValue();
	
	        if(nextToken()!=0xff)error("Expected end of input found "+flush());
	
	        return val;
	
	    }
	
	    
		/**
		 * 获取下一个字符,返回该字符的16进制值
		 * */
	    private function nextChar():int {
	
	        if(cachedChr){
	
	            cachedChr=false;
	
	            return chr;
	
	        }
	
	        return chr=src.charCodeAt(nextPos++);
	
	    }
	
		private function nextToken1() : JSONToken
		{
			var _token:* = this.tokenizer.getNextToken();
			this.token = this.tokenizer.getNextToken();
			return _token;
		}
	
	    private function nextToken():int {
	
	        if(cachedTok){
	
	            cachedTok=false;
	
	            return tok;
	
	        }
	
	        while(nextChar()==0x20||chr==0x09||isNewline(chr));
	
	        if(chr==0x2f){//char == "/"
	
	            if(nextChar()==0x2f){
	
	                while(!isNewline(nextChar())){
	
	                    if(chr==0x00)return tok=0xff;
	
	                }
	
	            }
	
	            else if(chr==0x2a){//char == "*"
	
	                while(true){
	
	                    if(nextChar()==0x2a){
	
	                        if(nextChar()==0x2f)break;
	
	                        else if(chr==0x2a)cachedChr=true;
	
	                    }
	
	                    if(chr==0x00)error("Find /* but cannot find */");
	
	                }
	
	            }
	
	            else error("Unkown token /"+String.fromCharCode(chr));
	
	            return nextToken();
	
	        }
	
	        lastPos=nextPos-1;
	
	        if(chr==0x22||chr==0x27)return tok=0xfc;// char == " " "||char == " ' "
	
	        if(chr==0x5d)return tok=0xfb;//char == "]"
	
	        if(chr==0x5b)return tok=0xfa;//char == "["
	
	        if(chr==0x7d)return tok=0xf9;//char == "}"
	
	        if(chr==0x7b)return tok=0xf8;//char == "{"
	
	        if(chr==0x2c)return tok=0xf6;//char == ","
	
	        if(chr==0x3a)return tok=0xf7;//char == ":"
	
	        if(chr==0x2b)return tok=0xf4;//char == "+"
	
	        if(chr==0x2d)return tok=0xf5;//char == "-"
	
	        if(chr==0x00)return tok=0xff;//char == "nul"
	
	        if(chr==0x2e){//char == "."
	
	            if(!isDigit(nextChar()))error("Find . but cannot find Digit");
	
	            return nextFraction();
	
	        }
	
	        if(isDigit(chr)){
	
	            if(chr==0x30){
	
	                if(nextChar()!=0x78&&chr!=0x58)cachedChr=true;
	
	                else{
	
	                    if(!isHex(nextChar()))error("Find 0x or 0X but cannot find HexDigit");
	
	                    while(isHex(nextChar()));
	
	                    return cache(0xfe);
	
	                }
	
	            }
	
	            while(true){
	
	                if(nextChar()==0x2e)return nextFraction();
	
	                if(chr==0x65||chr==0x45)return nextExponent();
	
	                if(!isDigit(chr))break;
	
	            }
	
	            return cache(0xfe);
	
	        }
	
	        
	
	        if(!isIdentifier(chr))error("Unkown token "+flush());
	
	        while(isIdentifier(nextChar()));
	
	        return cache(0xfd);
	
	    }
	
	    
	
	    private function nextValue():* {
	
	        if(nextToken()==0xfd){
	
	            var str:String=flush(1);
	
	            if(str=="NaN")return NaN;
	
	            if(str=="null")return null;
	
	            if(str=="true")return true;
	
	            if(str=="false")return false;
	
	            if(str=="Infinity")return Infinity;
	
	            if(str=="undefined")return undefined;
	
	            error("Unkown identifier "+str);
	
	        }
	
	        if(tok==0xf8){
	
	            var obj:Object={};
	
	            if(nextToken()!=0xf9){
	
	                cachedTok=true;
	
	                while (true){
	
	                    var key:String;
	
	                    if(nextToken()==0xfd)key=flush(1);
	
	                    else if(tok==0xfc)key=nextString();
	
	                    else error("Unexpected token "+flush());
	
	                    if(nextToken()==0xf7)obj[key]=nextValue();
	
	                    else error("Expected token : found "+flush());
	
	                    if(nextToken()==0xf9)break;
	
	                    if(tok!=0xf6)error("Expected token } or , found "+flush());
	
	                }
	
	            }
	
	            return obj;
	
	        }
	
	        if(tok==0xfa){
	
	            var arr:Array=[];
	
	            if(nextToken()!=0xfb){
	
	                var needComma:Boolean=false;
	
	                var index:int=0;
	
	                cachedTok=true;
	
	                while(true){
	
	                    if(nextToken()==0xfb)break;
	
	                    if(tok==0xf6){
	
	                        arr.length=++index;
	
	                        needComma=false;
	
	                    }
	
	                    else if(needComma)error("Expected token  ] or , found "+flush());
	
	                    else{
	
	                        needComma=true;
	
	                        cachedTok=true;
	
	                        arr[index]=nextValue();
	
	                    }
	
	                }
	
	            }
	
	            return arr;
	
	        }
	
	        if(tok==0xf5)return -nextValue();
	
	        if(tok==0xfc)return nextString();
	
	        if(tok==0xfe)return Number(flush(1));
	
	        if(tok==0xff)error("End of input was encountered");
	
	        if(tok!=0xf4)error("Unexpected token "+flush());
	
	        return nextValue();
	
	    }
	
	    
	
	    private function nextString():String {
	
	        lastPos=nextPos;
	
	        var str:String="";
	
	        var tag:int=chr;
	
	        while(nextChar()!=tag){
	
	            if(chr==0x00||isNewline(chr))error("Unclosed string");
	
	            if(chr==0x5c){
	
	                str+=flush(1);
	
	                lastPos+=2;
	
	                if(nextChar()==0x75||chr==0x78){
	
	                    var n:int=chr==0x75?4:2;
	
	                    while(n>0&&isHex(nextChar()))n--;
	
	                    if(n==0)str+=String.fromCharCode(parseInt(flush(),16));
	
	                    else nextPos=--lastPos;
	
	                }
	
	                else if(chr==0x6e)str+="\n";
	
	                else if(chr==0x72)str+="\r";
	
	                else if(chr==0x62)str+="\b";
	
	                else if(chr==0x66)str+="\f";
	
	                else if(chr==0x74)str+="\t";
	
	                else lastPos--;
	
	            }
	
	        }
	
	        return str+flush(1);
	
	    }
	
	    
		/**
		 * 获取这个小数
		 * */
	    private function nextFraction():int {
	
	        while(true){
	
	            if(nextChar()==0x65||chr==0x45)return nextExponent();
	
	            if(!isDigit(chr))break;
	
	        }
	
	        return cache(0xfe);
	
	    }
	
	    
		/**
		 * 科学计数法表示的数10.02E
		 * */
	    private function nextExponent():int {
	
	        if(nextChar()!=0x2b&&chr!=0x2d)cachedChr=true;
	
	        if(!isDigit(nextChar()))error("Need digit after exponent");
	
	        while (isDigit(nextChar()));
	
	        return cache(0xfe);
	
	    }
	
	    
	
	    private function cache(token:int):int {
	
	        cachedChr=true;
	
	        return tok=token;
	
	    }
	
	    
		/**
		 * 截取这段字符串
		 * */
	    private function flush(back:int=0):String {
	
	        return src.substring(lastPos,lastPos=nextPos-back);
	
	    }
	
	    
	
	    private function error(text:String):void {
	
	        throw new Error(text);
	
	    }
	
	    
		/**
		 * 是16进制的数吗
		 * */
	    private function isHex(c:int):Boolean {
	
	        return isDigit(c)||(c>0x60&&c<0x67)||(c>0x40&&c<0x47);
	
	    }
	
	    
		/**
		 * 判断是不是数字  c>0&&c<9
		 * */
	    private function isDigit(c:int):Boolean {
	
	        return c>0x2f&&c<0x3a;
	
	    }
	
	    
		/**
		 * 是不是换行键或退位键
		 * */
	    private function isNewline(c:int):Boolean {
	
	        return c==0x0a||c==0x0d;
	
	    }
	
	    
	
	    private function isIdentifier(c:int):Boolean {
	
	        if(isDigit(c))return true;
	
	        if(c>0x60&&c<0x7b)return true;
	
	        if(c>0x40&&c<0x5b)return true;
	
	        if(c==0x5f||c==0x24)return true;
	
	        if(c==0xd7||c==0xf7)return false;
	
	        if(c<0x00c0||c>0xfaff)return false;
	
	        if(c>0x00d6&&c<0x00d8)return false;
	
	        if(c>0x00f6&&c<0x00f8)return false;
	
	        if(c>0x1fff&&c<0x3040)return false;
	
	        if(c>0x318f&&c<0x3300)return false;
	
	        if(c>0x337f&&c<0x3400)return false;
	
	        if(c>0x3d2d&&c<0x4e00)return false;
	
	        if(c>0x9fff&&c<0xf900)return false;
	
	        return true;
	    }
		
		private function parseValue() : Object
		{
			if (this.token == null)
			{
				this.tokenizer.parseError("Unexpected end of input");
			}
			switch(this.token.type)
			{
				case JSONTokenType.LEFT_BRACE:
				{
					return this.parseObject();
				}
				case JSONTokenType.LEFT_BRACKET:
				{
					return this.parseArray();
				}
				case JSONTokenType.STRING:
				case JSONTokenType.NUMBER:
				case JSONTokenType.TRUE:
				case JSONTokenType.FALSE:
				case JSONTokenType.NULL:
				{
					return this.token.value;
				}
				default:
				{
					this.tokenizer.parseError("Unexpected " + this.token.value);
					break;
				}
			}
			return null;
		}
		
		private function parseObject() : Object
		{
			var _loc_2:String = null;
			var _loc_1:* = new Object();
			this.nextToken1();
			if (this.token.type == JSONTokenType.RIGHT_BRACE)
			{
				return _loc_1;
			}
			while (true)
			{
				
				if (this.token.type == JSONTokenType.STRING)
				{
					_loc_2 = String(this.token.value);
					this.nextToken1();
					if (this.token.type == JSONTokenType.COLON)
					{
						this.nextToken1();
						_loc_1[_loc_2] = this.parseValue();
						this.nextToken();
						if (this.token.type == JSONTokenType.RIGHT_BRACE)
						{
							return _loc_1;
						}
						if (this.token.type == JSONTokenType.COMMA)
						{
							this.nextToken1();
						}
						else
						{
							this.tokenizer.parseError("Expecting } or , but found " + this.token.value);
						}
					}
					else
					{
						this.tokenizer.parseError("Expecting : but found " + this.token.value);
					}
					continue;
				}
				this.tokenizer.parseError("Expecting string but found " + this.token.value);
			}
			return null;
		}
		
		public function getValue()
		{
			return this.value;
		}
		
		private function parseArray() : Array
		{
			var _loc_1:Array = [];
			this.nextToken1();
			if (this.token.type == JSONTokenType.RIGHT_BRACKET)
			{
				return _loc_1;
			}
			while (true)
			{
				
				_loc_1.push(this.parseValue());
				this.nextToken1();
				if (this.token.type == JSONTokenType.RIGHT_BRACKET)
				{
					return _loc_1;
				}
				if (this.token.type == JSONTokenType.COMMA)
				{
					this.nextToken1();
					continue;
				}
				this.tokenizer.parseError("Expecting ] or , but found " + this.token.value);
			}
			return null;
		}
	}
}